package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.qo.QueryCourseModel;
import com.xuecheng.api.system.model.dto.CourseBaseDTO;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseChargeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.CourseBaseConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CourseMarket;
import com.xuecheng.content.mapper.CourseBaseMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.CourseMarketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.Collections;
import java.util.List;

/**
 * <p>
 * 课程基本信息 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class CourseBaseServiceImpl extends ServiceImpl<CourseBaseMapper, CourseBase> implements CourseBaseService {

    @Autowired
    private CourseMarketService marketService;

    /**
     * 业务分析
     * 1、分页查询 课程基本信息（CourseBase）集合数据
     * 通过mp分页插件完成
     * 2、根据课程名称和审核状态条件进行数据查询
     * lambdaQueryWrapper
     * 3、教学机构职能查询到属于自己机构下的课程基本信息
     * <p>
     * 4、接口基于Http 请求，响应Json数据
     */
    @Override
    public PageVO queryCourseBaseList(PageRequestParams params, QueryCourseModel model, Long companyId) {

        /**
         * 1.判断业务数据
         * 1.1判断分页数据
         */
        if (params.getPageNo() < 1) {
            params.setPageNo(PageRequestParams.DEFAULT_PAGE_NUM);
        }
        if (params.getPageSize() < 1) {
            params.setPageSize(PageRequestParams.DEFAULT_PAGE_SIZE);
        }
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
        //1.2判断条件查询数据 如果查询条件有数据 在sql中添加条件
        //if (StringUtil.isNotBlank(model.getAuditStatus())){
        //    //添加审核状态条件有具体的值(eq)
        //}
        //if (StringUtil.isNotBlank(model.getCourseName())){
        //    //添加课程名称条件(like)
        //}

        queryWrapper.eq(StringUtil.isNotBlank(model.getAuditStatus()), CourseBase::getAuditStatus, model.getAuditStatus());
        queryWrapper.like(StringUtil.isNotBlank(model.getCourseName()), CourseBase::getName, model.getCourseName());
        queryWrapper.eq(CourseBase::getCompanyId, companyId);
        //2.创建分页数据
        Page<CourseBase> page = new Page<>(params.getPageNo(), params.getPageSize());
        //3.根据分页和查询条件查询list数据
        Page<CourseBase> courseBasePage = this.page(page, queryWrapper);
        List<CourseBase> list = courseBasePage.getRecords();

        List<CourseBaseDTO> courseBaseDTOS = Collections.emptyList();
        if (!(CollectionUtils.isEmpty(list))) {
            courseBaseDTOS = CourseBaseConvert.INSTANCE.entity2dtos(list);
        }

        long total = courseBasePage.getTotal();
        //4.将查询结果封装返回
        return new PageVO(courseBaseDTOS, total, params.getPageNo(), params.getPageSize());

    }

    /**
     * 1.是否开启事务
     * 2.判断关键的业务数据
     * 前端来判断 : 课程名称 课程分类 课程适用人群 课程类型
     * 数据库来判断 公司id 课程名称 课程分类 课程等级 教学模式 审核状态
     * 3.将dto数据转为po
     * 4.保存数据并判断结果
     * 1.课程基本信息
     * 2.课程营销信息
     * 5.将po数据转换为dto数据并返回
     * ps 统一抛出业务异常  审核状态是后端控制的 并不是前端传入的
     */
    @Override
    //1.由于是插入数据 所以需要开启事务
    @Transactional
    public CourseBaseDTO createCourseBase(CourseBaseDTO dto) {

        //2.判断关键的业务数据
        this.verifyCourseMsg(dto);

        //3.将dto转为po  利用mapstruct
        CourseBase courseBase = CourseBaseConvert.INSTANCE.dto2entity(dto);
        //给审核状态赋值
        courseBase.setAuditStatus(CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode());

        //4.保存数据并判断结果
        boolean result = this.save(courseBase);

        if (!result) {
            throw new RuntimeException("保存基础课程信息失败");
        }
        //5.保存课程营销数据
        CourseMarket courseMarket = new CourseMarket();

        courseMarket.setCharge(dto.getCharge());

        //如果是收费课程 那么插入price
        if (CourseChargeEnum.CHARGE_YES.getDesc().equals(dto.getCharge())) {

            courseMarket.setPrice(dto.getPrice().floatValue());


        }
        //  MP 插入数据后id 回填
        courseMarket.setCourseId(courseBase.getId());

        boolean marketResult = this.marketService.save(courseMarket);

        if (!marketResult) {
            throw new RuntimeException("保存营销课程信息失败");
        }
        //将po数据转换为dto数据并返回
        CourseBaseDTO courseBaseDTO = CourseBaseConvert.INSTANCE.entity2dto(courseBase);
        if (CourseChargeEnum.CHARGE_YES.getDesc().equals(dto.getCharge())) {
            courseBaseDTO.setPrice(new BigDecimal(courseMarket.getPrice()));
        }
        courseBaseDTO.setAuditStatus(courseMarket.getCharge());
        return courseBaseDTO;
    }

    /**
     * @param courseBaseId 根据id获取课程基本信息
     * @param companyId
     * @return 1.先判断是否需要开启事务
     * 2.对关键数据进行判断
     * 3.根据id查询课程基本信息
     * 4.判断课程是否存在 如果存在再判断是否删除
     * 5.查询营销数据判断是否存在
     * 6.转换数据类型并返回
     */
    //1.因为是查询所以不需要开启事务
    @Override
    public CourseBaseDTO getCourseBase(Long courseBaseId, Long companyId) {
        //2.对关键数据进行判断
        if (ObjectUtils.isEmpty(courseBaseId) || ObjectUtils.isEmpty(companyId)) {
            throw new RuntimeException("关键数据不匹配");
        }
        //根据id查询课程基本信息
        CourseBase courseBase = this.getOne(new LambdaQueryWrapper<CourseBase>()
                .eq(CourseBase::getCompanyId, companyId)
                .eq(CourseBase::getId, courseBaseId));
        //判断课程信息是否存在
        if (ObjectUtils.isEmpty(courseBase)) {
            throw new RuntimeException("课程信息不存在");
        }
        //判断课程信息是否删除
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(courseBase.getStatus()))) {
            throw new RuntimeException("课程已经被删除");
        }
        //查询营销数据是否存在
        LambdaQueryWrapper<CourseMarket> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CourseMarket::getCourseId, courseBaseId);
        CourseMarket courseMarket = this.marketService.getOne(queryWrapper);
        if (ObjectUtils.isEmpty(courseMarket)) {
            throw new RuntimeException("营销数据不存在");
        }
        //转换数据并返回
        CourseBaseDTO courseBaseDTO = CourseBaseConvert.INSTANCE.entity2dto(courseBase);
        if (!(ObjectUtils.isEmpty(courseMarket.getPrice()))) {
            courseBaseDTO.setPrice(new BigDecimal(courseMarket.getPrice()));
        }
        courseBaseDTO.setCharge(courseMarket.getCharge());
        return courseBaseDTO;
    }

    /**
     * @param dto 更新课程基本信息
     * @return 1.是否开启事务
     * 2.对关键数据进行判断  判断要修改的课程信息的id是否存在对应的课程信息  判断修改的数据是否有id 没id不就成新增了么
     * 3.判断是否已经被删除
     * 4.判断审核的状态
     * 5.判断课程是否为同一家教学机构
     * 6.修改课程数据  基本数据 和 营销数据
     * 7.将修改后的数据返回
     */
    @Override
    //因为是修改数据所以要开启事务
    @Transactional
    public CourseBaseDTO modifyCourse(CourseBaseDTO dto) {
        //判断关键数据
        this.verifyCourseMsg(dto);
        //判断课程id
        if (ObjectUtils.isEmpty(dto.getCourseBaseId())) {
            throw new RuntimeException("课程id不能为空");
        }
        //判断要修改的课程是否存在
        CourseBase courseBase = this.getById(dto.getCourseBaseId());

        if (ObjectUtils.isEmpty(courseBase)) {
            throw new RuntimeException("课程信息不存在");
        }
        //判断课程的状态是否被删除
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(courseBase.getStatus()))) {
            throw new RuntimeException("课程信息已经被删除");
        }
        //判断课程的审核状态 只有未提交和审核未通过是可以更改课程数据的
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_UNLINE_STATUS.getCode().equals(auditStatus)
        ) {
            throw new RuntimeException("课程已提交或审核,无法修改");
        }
        //判断要修改的课程是不是同一家机构的课程
        if (!(ObjectUtils.nullSafeEquals(dto.getCompanyId(), courseBase.getCompanyId()))) {
            throw new RuntimeException("无权访问");
        }
        //将dto转换为pojo 进行修改数据
        CourseBase entity = CourseBaseConvert.INSTANCE.dto2entity(dto);
        boolean result = this.updateById(entity);
        //修改失败抛出异常
        if (!result) {
            throw new RuntimeException("课程修改失败");
        }
        //根据课程的id修改课程的营销数据 修改失败 抛出异常
        LambdaUpdateWrapper<CourseMarket> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CourseMarket::getCharge, dto.getCharge());
        updateWrapper.set(CourseMarket::getPrice, dto.getPrice());
        updateWrapper.eq(CourseMarket::getCourseId, dto.getCourseBaseId());
        boolean updateResult = this.marketService.update(updateWrapper);
        if (!updateResult) {
            throw new RuntimeException("营销数据修改失败");
        }
        //修改成功 返回dto
        return dto;
    }

    /**
     * @param courseBaseId 课程id
     * @param companyId    机构id
     *                     1.是否开启事务
     *                     2.判断前端传过来的数据
     *                     3.判断关键数据
     *                     4.判断课程的状态
     *                     5.是否为同一家机构
     *                     6.删除基本数据和营销数据
     *
     */
    @Override
    //因为是删除 所以要开启事务
    @Transactional
    public void removeCoursebase(Long courseBaseId, Long companyId) {
        //判断前端传过来的数据
        if (ObjectUtils.isEmpty(courseBaseId)||ObjectUtils.isEmpty(companyId)){
            throw new RuntimeException("数据不合法");
        }
        //判断关键数据
        CourseBase courseBase = this.getById(courseBaseId);
        if (ObjectUtils.isEmpty(courseBase)){
            throw new RuntimeException("课程信息不存在");
        }
        //判断课程的状态是否被删除
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(courseBase.getStatus()))) {
            throw new RuntimeException("课程信息已经被删除");
        }
        //判断课程是否处于可以被删除的状态
        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_COMMIT_STATUS.getDesc().equals(auditStatus)||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getDesc().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getDesc().equals(auditStatus)){
            throw new RuntimeException("课程状态异常");
        }
        //判断课程是否属于该机构
        if (!(ObjectUtils.nullSafeEquals(companyId,courseBase.getCompanyId()))){
            throw new RuntimeException("无权访问");
        }
        //进行逻辑删除
        LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CourseBase::getStatus,CommonEnum.DELETE_FLAG.getCodeInt());
        //修改更新的时间
        updateWrapper.set(CourseBase::getChangeDate, LocalTime.now());
        //根据查出来的coursebase的id 进行删除 不要用传过来的id进行删除
        updateWrapper.eq(CourseBase::getId,courseBase.getId());
        boolean resule = this.update(updateWrapper);
        //删除失败抛出异常
        if (!resule){
            throw new RuntimeException("删除信息失败");
        }
    }

    /**
     * 校验课程基础信息（抽取方法的快捷键：ctrl+alt+M）
     *
     * @param baseDTO
     */
    private void verifyCourseMsg(CourseBaseDTO baseDTO) {
        // 1.判断关键的业务数据
        if (StringUtil.isBlank(baseDTO.getName())) {
            //throw new RuntimeException("课程名称不能为空");
            ExceptionCast.cast(ContentErrorCode.E_120004);
        }
        if (StringUtil.isBlank(baseDTO.getMt())) {
            //throw new RuntimeException("课程大分类不能为空");
            ExceptionCast.cast(ContentErrorCode.E_120002);
        }
        if (StringUtil.isBlank(baseDTO.getSt())) {
            //throw new RuntimeException("课程小分类不能为空");
            ExceptionCast.cast(ContentErrorCode.E_120003);
        }
        if (ObjectUtils.isEmpty(baseDTO.getCompanyId())) {
            //throw new RuntimeException("公司id不能为空");
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }
        if (StringUtil.isBlank(baseDTO.getGrade())) {
            //throw new RuntimeException("课程等级不能为空");
            ExceptionCast.cast(ContentErrorCode.E_120007);
        }
        if (StringUtil.isBlank(baseDTO.getTeachmode())) {
            //throw new RuntimeException("课程授课模式不能为空");
            ExceptionCast.cast(ContentErrorCode.E_120006);
        }
    }
}
